package edu.zju.gis.dldsj.utils;

import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author yanlong_lee@qq.com
 * @version 1.0 2018/11/04
 */
public final class GeometryUtil {
    public static final String EMPTY_GeoJSON = "{\"type\":\"FeatureCollection\",\"features\":[]}";
    private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();//用于构造geometry
    private static final FeatureJSON FEATURE_JSON = new FeatureJSON();

    public static JSONObject toGeoJSON(JSONArray jsons, String featureName) throws IOException {
        if (jsons == null || jsons.length() == 0)
            return new JSONObject(EMPTY_GeoJSON);
        Set<String> keys = jsons.getJSONObject(0).keySet();
        if (keys.contains("lon") && keys.contains("lat")) {
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(featureName);//设置feature类型名，必填
            for (String field : keys) {
                switch (field) {
                    case "lon":
                    case "lat":
                        break;
                    default:
                        typeBuilder.add(field, String.class);//统一构造属性字段
                }
            }
            typeBuilder.add("geometry", Point.class);//根据数据类型构造geometry字段
            DefaultFeatureCollection sfc = new DefaultFeatureCollection(null, typeBuilder.buildFeatureType());
            for (int i = 0; i < jsons.length(); i++) {
                JSONObject json = jsons.getJSONObject(i);
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(sfc.getSchema());
                for (String field : keys) {
                    switch (field) {
                        case "lon":
                        case "lat":
                            break;
                        default:
                            featureBuilder.add(json.optString(field));//按顺序添加属性值
                    }
                }
                featureBuilder.add(GEOMETRY_FACTORY.createPoint(new Coordinate(json.optDouble("lon"), json.optDouble("lat"))));
                sfc.add(featureBuilder.buildFeature(null));//为feature生成默认ID
            }
            JSONObject geojson = new JSONObject(FEATURE_JSON.toString(sfc));
            ReferencedEnvelope env = sfc.getBounds();
            return geojson.put("bbox", new double[]{env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY()});
        } else if (keys.contains("start_lon") && keys.contains("start_lat")
                && keys.contains("end_lon") && keys.contains("end_lat")) {
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(featureName);//设置feature类型名，必填
            for (String field : keys) {
                switch (field) {
                    case "start_lon":
                    case "start_lat":
                    case "end_lon":
                    case "end_lat":
                        break;
                    default:
                        typeBuilder.add(field, String.class);//统一构造属性字段
                }
            }
            typeBuilder.add("geometry", LineString.class);//根据数据类型构造geometry字段
            DefaultFeatureCollection sfc = new DefaultFeatureCollection(null, typeBuilder.buildFeatureType());
            for (int i = 0; i < jsons.length(); i++) {
                JSONObject json = jsons.getJSONObject(i);
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(sfc.getSchema());
                for (String field : keys) {
                    switch (field) {
                        case "start_lon":
                        case "start_lat":
                        case "end_lon":
                        case "end_lat":
                            break;
                        default:
                            featureBuilder.add(json.getString(field));//按顺序添加属性值
                    }
                }
                featureBuilder.add(GEOMETRY_FACTORY.createLineString(new Coordinate[]{
                        new Coordinate(json.optDouble("start_lon"), json.optDouble("start_lat")),
                        new Coordinate(json.optDouble("end_lon"), json.optDouble("end_lat"))
                }));
                sfc.add(featureBuilder.buildFeature(null));//为feature生成默认ID
            }
            JSONObject geojson = new JSONObject(FEATURE_JSON.toString(sfc));
            ReferencedEnvelope env = sfc.getBounds();
            return geojson.put("bbox", new double[]{env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY()});
        } else {
            return new JSONObject(EMPTY_GeoJSON);
        }
    }

    /**
     * @param models      要素集合，每个元素作为一个要素，根据其字段名判读其要素类型。lon,lat->point;start_lon,start_lat,end_lon,end_lat->line
     * @param featureName 要素集合名
     * @return GeometryCollection，当集合为空时，返回空的GeometryCollection
     */
    public static JSONObject toGeoJSON(List<?> models, String featureName) throws IOException, IllegalAccessException, ParseException {
        if (models == null || models.isEmpty())
            return new JSONObject(EMPTY_GeoJSON);
        Object record = models.get(0);
        Field[] fields = record.getClass().getDeclaredFields();
        List<String> fieldNames = Arrays.stream(fields).map(Field::getName).collect(Collectors.toList());
        Arrays.stream(fields).forEach(f -> f.setAccessible(true));
        if (fieldNames.contains("lon") && fieldNames.contains("lat")) {
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(featureName);//设置feature类型名，必填
            for (String field : fieldNames) {
                switch (field) {
                    case "lon":
                    case "lat":
                        break;
                    default:
                        typeBuilder.add(field, String.class);//统一构造属性字段
                }
            }
            typeBuilder.add("geometry", Point.class);//根据数据类型构造geometry字段
            DefaultFeatureCollection sfc = new DefaultFeatureCollection(null, typeBuilder.buildFeatureType());
            for (Object model : models) {
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(sfc.getSchema());
                double lon = 0;
                double lat = 0;
                for (Field field : fields) {
                    switch (field.getName()) {
                        case "lon":
                            lon = field.getDouble(model);
                            break;
                        case "lat":
                            lat = field.getDouble(model);
                            break;
                        default:
                            featureBuilder.add(field.get(model).toString());//按顺序添加属性值
                    }
                }
                featureBuilder.add(GEOMETRY_FACTORY.createPoint(new Coordinate(lon, lat)));
                sfc.add(featureBuilder.buildFeature(null));//为feature生成默认ID
            }
            JSONObject geojson = new JSONObject(FEATURE_JSON.toString(sfc));
            ReferencedEnvelope env = sfc.getBounds();
            return geojson.put("bbox", new double[]{env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY()});
        } else if (fieldNames.contains("start_lon") && fieldNames.contains("start_lat")
                && fieldNames.contains("end_lon") && fieldNames.contains("end_lat")) {
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(featureName);//设置feature类型名，必填
            for (String field : fieldNames) {
                switch (field) {
                    case "start_lon":
                    case "start_lat":
                    case "end_lon":
                    case "end_lat":
                        break;
                    default:
                        typeBuilder.add(field, String.class);//统一构造属性字段
                }
            }
            typeBuilder.add("geometry", LineString.class);//根据数据类型构造geometry字段
            DefaultFeatureCollection sfc = new DefaultFeatureCollection(null, typeBuilder.buildFeatureType());
            for (Object model : models) {
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(sfc.getSchema());
                double start_lon = 0, start_lat = 0, end_lon = 0, end_lat = 0;
                for (Field field : fields) {
                    switch (field.getName()) {
                        case "start_lon":
                            start_lon = field.getDouble(model);
                            break;
                        case "start_lat":
                            start_lat = field.getDouble(model);
                            break;
                        case "end_lon":
                            end_lon = field.getDouble(model);
                            break;
                        case "end_lat":
                            end_lat = field.getDouble(model);
                            break;
                        default:
                            featureBuilder.add(field.get(model).toString());//按顺序添加属性值
                    }
                }
                featureBuilder.add(GEOMETRY_FACTORY.createLineString(new Coordinate[]{
                        new Coordinate(start_lon, start_lat), new Coordinate(end_lon, end_lat)}));
                sfc.add(featureBuilder.buildFeature(null));//为feature生成默认ID
            }
            JSONObject geojson = new JSONObject(FEATURE_JSON.toString(sfc));
            ReferencedEnvelope env = sfc.getBounds();
            return geojson.put("bbox", new double[]{env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY()});
        } else if (fieldNames.contains("geom")) {
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(featureName);//设置feature类型名，必填
            WKTReader reader = new WKTReader();
            Field geomField = null;
            for (Field field : fields) {
                switch (field.getName()) {
                    case "geom":
                        geomField = field;
                        break;
                    default:
                        typeBuilder.add(field.getName(), String.class);//统一构造属性字段
                }
            }
            //根据数据类型构造geometry字段
            assert geomField != null;
            typeBuilder.add("geometry", reader.read(geomField.get(record).toString()).getClass());
            DefaultFeatureCollection sfc = new DefaultFeatureCollection(null, typeBuilder.buildFeatureType());
            for (Object model : models) {
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(sfc.getSchema());
                for (Field field : fields) {
                    switch (field.getName()) {
                        case "lon":
                        case "lat":
                        case "geom":
                            break;
                        default:
                            featureBuilder.add(field.get(model));//按顺序添加属性值
                    }
                }
                Geometry geometry = reader.read(geomField.get(model).toString());
                featureBuilder.add(geometry);
                sfc.add(featureBuilder.buildFeature(null));
            }
            JSONObject geojson = new JSONObject(FEATURE_JSON.toString(sfc));
            ReferencedEnvelope env = sfc.getBounds();
            return geojson.put("bbox", new double[]{env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY()});
        } else {
            return new JSONObject(EMPTY_GeoJSON);
        }
    }

}
